package com.dnndo.web.controller.wx;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dnndo.common.constant.RedisConstant;
import com.dnndo.common.constant.RoleConstants;
import com.dnndo.common.constant.ScanStatusConstants;
import com.dnndo.common.core.domain.R;
import com.dnndo.common.core.domain.entity.SysRole;
import com.dnndo.common.core.domain.entity.SysUser;
import com.dnndo.common.core.redis.RedisCache;
import com.dnndo.common.utils.SecurityUtils;
import com.dnndo.common.utils.http.HttpUtils;
import com.dnndo.framework.web.service.SysLoginService;
import com.dnndo.system.domain.SysUserRole;
import com.dnndo.system.mapper.SysUserRoleMapper;
import com.dnndo.system.service.ISysRoleService;
import com.dnndo.system.service.ISysUserService;
import com.dnndo.wx.domain.dto.LoginBindPhoneDTO;
import com.dnndo.wx.domain.vo.CodeGetWxInfoVO;
import com.dnndo.wx.domain.vo.LoginBindPhoneVO;
import com.dnndo.wx.domain.vo.ScanStatusVO;
import com.dnndo.wx.domain.vo.WxUserInfoVO;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiParam;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import lombok.extern.log4j.Log4j2;
import org.apache.poi.ss.formula.functions.T;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.TimeUnit;

/**
 * 微信开放平台接口
 */
@RestController
@RequestMapping("/wx/open")
@Log4j2
public class WeChatOpenController {
    @Value("${wx.open.app_id}")
    private String appId;
    @Value("${wx.open.secret}")
    private String secret;
    @Resource
    private ISysUserService userService;
    @Resource
    private SysLoginService loginService;
    @Resource
    private RedisCache redisCache;
    @Autowired
    private ISysRoleService roleService;
    @Autowired
    private SysUserRoleMapper userRoleMapper;
    /**
     * 扫码根据code登录
     */
    @GetMapping("/scanLoginByCode")
    public R<ScanStatusVO> scanLoginByCode(@ApiParam(name = "code", value = "请求微信获取的code", required = true) String code) {

        if (code == null || code.isEmpty()) {
            return R.fail("参数不可为空");
        }

        //拼接请求地址
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?"
                + "appid=" + appId
                + "&secret=" + secret
                + "&code=" + code
                + "&grant_type=authorization_code";

        //发送请求
        String result;
        try {
            result = HttpUtils.getCall(url);
        } catch (Exception e) {
            log.error("发送http请求失败：请求地址：{} 错误信息:{}", url,e.getMessage());
            return R.fail("登录失败");
        }

        //初始化对象
        CodeGetWxInfoVO infoVO = null;
        if (result != null && !result.isEmpty()) {
            //将返回结果转换为实体类
            infoVO = JSON.to(CodeGetWxInfoVO.class, result);
        }

        //判断是否获取成功
        if (infoVO != null && infoVO.getUnionid() != null && !infoVO.getUnionid().isEmpty()) {

            //根据unionid从数据库查询是否存在用户
            SysUser user = userService.getOne(new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getUnionid, infoVO.getUnionid()));

            //如果存在则直接登录，并返回token
            if (user != null) {
                ScanStatusVO statusVO = new ScanStatusVO(ScanStatusConstants.LOGIN_SUCCESS, infoVO.getUnionid());
                statusVO.setToken(loginService.scanLogin(user.getUserName()));
                return R.ok(statusVO);
            } else {
                //如果不存在则返回需要绑定手机号
                ScanStatusVO statusVO = new ScanStatusVO(ScanStatusConstants.SCANNED_SUCCESS, infoVO.getUnionid());
                //unionid为键存储infoVO
                WxUserInfoVO wxUserInfo = getWxUserInfo(infoVO.getAccess_token(), infoVO.getOpenid());
                if (wxUserInfo != null) {
                    redisCache.setCacheObject(RedisConstant.WX_SCAN_USERINFO + infoVO.getUnionid(), wxUserInfo,1, TimeUnit.HOURS);
                    statusVO.setAvatar(wxUserInfo.getHeadimgurl());
                    statusVO.setNickName(wxUserInfo.getNickname());
                }
                return R.ok(statusVO);
            }

        }
        return R.fail("登录失败");
    }
    /**
     * 登录时绑定手机号
     */
    @PostMapping("/loginBindPhone")
    public R<LoginBindPhoneVO> loginBindPhone(@RequestBody @Validated LoginBindPhoneDTO dto) {
        //初始化返回结果对象
        LoginBindPhoneVO vo = new LoginBindPhoneVO();
        vo.setNeedSetPwd(true);

        //验证短信验证码
        R<LoginBindPhoneVO> verified = verifySMSCode(dto);
        if (verified != null) {
            return verified;
        }

        //根据手机号查询用户信息
        SysUser user = userService.getOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getPhonenumber, dto.getPhone())
                .eq(SysUser::getDelFlag, 0)
                .orderByDesc(SysUser::getCreateTime)
                .last("limit 1"));

        //如果用户信息不为空则绑定，然后返回token
        if (user != null) {

            //判断是否需要设置密码
            if (user.getPassword() == null || user.getPassword().isEmpty()) {
                vo.setNeedSetPwd(true);
            }
            //修改用户信息
            user.setUnionid(dto.getUnionid());
            user.updateById();
            //返回token
            vo.setToken(loginService.scanLogin(dto.getPhone()));
            return R.ok(vo);

        } else {
            // 查询普通用户角色信息
            SysRole role = roleService.getOne(new LambdaQueryWrapper<SysRole>()
                    .eq(SysRole::getRoleType, RoleConstants.ROLE_TYPE_COMMON)
                    .eq(SysRole::getStatus, RoleConstants.ROLE_STATUS_NORMAL)
                    .eq(SysRole::getDelFlag, 0)
                    .orderByDesc(SysRole::getCreateTime)
                    .last("limit 1"));
            //如果没有普通角色，直接返回注册失败
            if (role == null) {
                return R.fail("登录失败,请联系系统管理人员");
            }
            //初始化微信用户信息
            WxUserInfoVO wxUserInfoVO = new WxUserInfoVO();
            //从Redis中取出微信扫码信息
            if (redisCache.hasKey(RedisConstant.WX_SCAN_USERINFO + dto.getUnionid())) {
                wxUserInfoVO = redisCache.getCacheObject(RedisConstant.WX_SCAN_USERINFO + dto.getUnionid());
            }
            //如果用户信息为空则创建用户，然后返回token
            user = new SysUser();
            user.setUserName(dto.getPhone());
            user.setPhonenumber(dto.getPhone());
            user.setUnionid(dto.getUnionid());
            user.setNickName(dto.getPhone());
            //写入头像和昵称
            if (wxUserInfoVO != null) {
                user.setNickName(wxUserInfoVO.getNickname());
                user.setAvatar(wxUserInfoVO.getHeadimgurl());
            }
            boolean insert = user.insert();
            if (insert) {
                // 注册成功后，将用户角色设置为普通用户
                // 插入用户角色
                SysUserRole userRole = new SysUserRole();
                userRole.setRoleId(role.getRoleId());
                userRole.setUserId(user.getUserId());
                userRoleMapper.insert(userRole);

                vo.setToken(loginService.scanLogin(dto.getPhone()));
                return R.ok(vo);
            } else {
                return R.fail("登录失败");
            }
        }
    }

    /**
     * 设置密码
     */
    @GetMapping("/setPwd")
    @ApiImplicitParam(name = "Authorization", value = "登录标识(token)", required = true, dataType = "String",paramType = "header")
    public R<?> setPwd(@ApiParam(name = "password", value = "密码", required = true)@NotBlank(message = "密码不可为空") String password) {
        //获取用户id
        Long userId = SecurityUtils.getUserId();
        //根据用户ID修改密码
        boolean update = userService.update(new LambdaUpdateWrapper<SysUser>()
                .eq(SysUser::getUserId, userId)
                .set(SysUser::getPassword, SecurityUtils.encryptPassword(password)));
        if (update) {
            return R.ok();
        } else {
            return R.fail("设置失败");
        }
    }

    /**
     * 绑定微信
     */
    @GetMapping("/bindWx")
    public R<?> bindWx(@ApiParam(name = "code", value = "请求微信获取的code", required = true)
                           @NotBlank(message = "code不可为空") String code) {
        //拼接请求地址
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?"
                + "appid=" + appId
                + "&secret=" + secret
                + "&code=" + code
                + "&grant_type=authorization_code";

        //发送请求
        String result;
        try {
            result = HttpUtils.getCall(url);
        } catch (Exception e) {
            log.error("发送http请求失败：请求地址：{} 错误信息:{}", url,e.getMessage());
            return R.fail("绑定失败");
        }

        //初始化对象
        CodeGetWxInfoVO infoVO = null;
        if (result != null && !result.isEmpty()) {
            //将返回结果转换为实体类
            infoVO = JSON.to(CodeGetWxInfoVO.class, result);
        }

        if (infoVO != null && infoVO.getUnionid() != null && !infoVO.getUnionid().isEmpty()) {
            //获取用户ID
            Long userId = SecurityUtils.getUserId();
            //根据ID修改用户信息
            boolean update = userService.update(new LambdaUpdateWrapper<SysUser>()
                    .eq(SysUser::getUserId, userId)
                    .set(SysUser::getUnionid, infoVO.getUnionid()));

            if (update) {
                return R.ok();
            } else {
                return R.fail("绑定失败");
            }
        }
        return R.fail("绑定失败");
    }

    /**
     * 验证短信验证码
     * @param dto 登录绑定手机号dto
     * @return 验证结果
     */
    private R<LoginBindPhoneVO> verifySMSCode(LoginBindPhoneDTO dto) {
        Boolean hasKey = redisCache.hasKey(RedisConstant.SMS_CODE_KEY + dto.getPhone());
        if (!hasKey) {
            return R.fail("验证码错误");
        } else {
            String smsCode = redisCache.getCacheObject(RedisConstant.SMS_CODE_KEY + dto.getPhone());
            if (!dto.getSmsCode().equals(smsCode)) {
                return R.fail("验证码错误");
            } else {
                redisCache.deleteObject(RedisConstant.SMS_CODE_KEY + dto.getPhone());
                redisCache.deleteObject(RedisConstant.SMS_CODE_COUNT_KEY + dto.getPhone());
            }
        }
        return null;
    }

    /**
     * 获取微信用户信息
     */
    private WxUserInfoVO getWxUserInfo(String accessToken, String openid) {
        //拼接请求地址
        String url = "https://api.weixin.qq.com/sns/userinfo?"
                + "access_token=" + accessToken
                + "&openid=" + openid
                + "&lang=zh_CN";
        //发送请求
        String result;
        try {
            result = HttpUtils.getCall(url);
        } catch (Exception e) {
            log.error("获取微信用户信息失败：请求地址：{} 错误信息:{}", url,e.getMessage());
            return null;
        }
        if (result != null && !result.isEmpty()) {
            //将返回结果转换为实体类
            return JSON.to(WxUserInfoVO.class, result);
        } else {
            return null;
        }
    }
}
